package fr.cnam.projetSiw.server.sparql;

import java.util.ArrayList;
import java.util.Collections;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.hp.hpl.jena.datatypes.xsd.XSDDateTime;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.sparql.engine.http.QueryEngineHTTP;

import de.umass.lastfm.ImageSize;
import fr.cnam.projetSiw.client.services.SparqlService;
import fr.cnam.projetSiw.server.lastFm.LastFmServiceImpl;
import fr.cnam.projetSiw.shared.Artist;
import fr.cnam.projetSiw.shared.City;
import fr.cnam.projetSiw.shared.Track;

@SuppressWarnings("serial")
public class SparqlServiceImpl extends RemoteServiceServlet implements SparqlService{

	private static final String TIMEOUT = "30000";
	private LastFmServiceImpl lastFmService = new LastFmServiceImpl();
	
	@Override
	public City getCityFromSongName(String songName) 
	{
		String queryStr = "PREFIX dbpedia:<http://dbpedia.org/ontology/> "
				+ "SELECT ?city WHERE { <http://dbpedia.org/resource/" +songName + "> dbpedia:recordedIn ?o."
				+ " ?o <http://dbpedia.org/property/name> ?city }";
	       Query query = QueryFactory.create(queryStr);

	       // Remote execution.
	       QueryExecution qexec = QueryExecutionFactory.sparqlService("http://dbpedia.org/sparql", query);
	       // Set the DBpedia specific timeout.
	       ((QueryEngineHTTP)qexec).addParam("timeout", TIMEOUT) ;

	       // Execute.
	       ResultSet rs = qexec.execSelect();
//	       ResultSetFormatter.out(System.out, rs, query);
	       
	       City city = new City();
	       while(rs.hasNext()){
	    	   city.setName(rs.next().getLiteral("?city").getString());
	       }
	       
	       return city;
	}
	
	@Override
	public ArrayList<Artist> getArtistsByCityNameAndGenre(String cityName, String genre){
		ArrayList<Artist> lstArtists = new ArrayList<Artist>();
		
		genre = genre.replaceAll(" ", "_");
		

		String genreSubQuery = (genre == null || genre == "") ? "?genre" : "<http://dbpedia.org/resource/" +genre+ ">";
		
		String queryStr = "PREFIX dbpedia:<http://dbpedia.org/ontology/>"
				+ "PREFIX prop:<http://dbpedia.org/property/>"
				+ "PREFIX frprop:<http://fr.dbpedia.org/property/>"
				+ "SELECT DISTINCT ?artisteName ?birthDate WHERE { "
				+ " ?artiste dbpedia:genre " + genreSubQuery +" ."
				+ "?artiste ?naissance <http://dbpedia.org/resource/" +cityName+ ">. "
				+ "FILTER (?naissance = dbpedia:birthPlace || ?naissance = dbpedia:hometown || ?naissance = prop:origin || ?naissance = prop:birthPlace || frprop:lieuDeNaissance)"
				+ "?artiste prop:name ?artisteName ."
				+ "?artiste dbpedia:birthDate ?birthDate } LIMIT 100";
				
	       Query query = QueryFactory.create(queryStr);
	       // Remote execution.
	       QueryExecution qexec = QueryExecutionFactory.sparqlService("http://dbpedia.org/sparql", query);
	       // Set the DBpedia specific timeout.
	       ((QueryEngineHTTP)qexec).addParam("timeout", TIMEOUT) ;

	    // Execute.
	       ResultSet rs = qexec.execSelect();
	       
	       while(rs.hasNext()){
	    	 QuerySolution sol = rs.next();
	    	 Artist artist = null;
	    	 try{
				artist = lastFmService.getArtistByName(sol.getLiteral("?artisteName").getString(), ImageSize.LARGE);
	    		 RDFNode dateNode = sol.get("?birthDate");
	    		 XSDDateTime o = (XSDDateTime) dateNode.asLiteral().getValue();
	    		 if(o != null && o.asCalendar() != null && artist != null)
	    			 artist.setBirthDate(o.asCalendar().getTime());
	    	 }
	    	 catch(ClassCastException e){
	    		 
	    	 }
	    	 if(artist != null)
	    	    lstArtists.add(artist);
	       }
	       Collections.sort(lstArtists);
	       return lstArtists;
	}
 
	@Override
	public ArrayList<Track> getSongNameRecordedInCityByGenre(String cityName, String genre)
	{
		
		ArrayList<Track> lstSong = new ArrayList<Track>();
		
		genre = genre.replaceAll(" ", "_");
		
		String queryStr = "PREFIX dbpedia:<http://dbpedia.org/ontology/>"
				+ "PREFIX property:<http://dbpedia.org/property/>"
				+ "SELECT ?songName ?artistName ?date WHERE { ?s dbpedia:recordedIn <http://dbpedia.org/resource/"+cityName +"> ."
						+ "?s dbpedia:genre <http://dbpedia.org/resource/" +genre+ "> ."
						+ "?s dbpedia:releaseDate ?date ." 
						+ "?s property:artist ?artist ."
						+ "?s  property:name ?songName ."
						+ "?artist property:name ?artistName } LIMIT 100";
		
	       Query query = QueryFactory.create(queryStr);
	       
	       // Remote execution.
	       QueryExecution qexec = QueryExecutionFactory.sparqlService("http://dbpedia.org/sparql", query);
	       // Set the DBpedia specific timeout.
	       ((QueryEngineHTTP)qexec).addParam("timeout", TIMEOUT) ;

	       // Execute.
	       ResultSet rs = qexec.execSelect();
	       
	       while(rs.hasNext()){
	    	 QuerySolution sol = rs.next();
	    	 
	    	 Track track = null;
	    	 try{
	    		 track = lastFmService.getTrackByName(sol.getLiteral("?artistName").getString(), sol.getLiteral("?songName").getString());
	    		 RDFNode dateNode = sol.get("?date");
	    		 XSDDateTime o = (XSDDateTime) dateNode.asLiteral().getValue();
	    		 if(o != null && o.asCalendar() != null && track != null)
	    			 track.setRealiseDate(o.asCalendar().getTime());

	    	 }
	    	 catch(ClassCastException e){
	    		 
	    	 }
	    	 if(track != null)
	    		 lstSong.add(track);
	    	 
	       }
	       
	       return lstSong;
	}

	/**
	 * Marche quasiment jamais, vu qu'il faut une lon et lat qui correspond
	 * parfaitement à wikipedia
	 */
	@Deprecated
	@Override
	public City getCityFromLngLat(String lon, String lat) {
		
			String queryStr = "PREFIX dbpedia2: <http://dbpedia.org/property/>"
				+ "PREFIX dbpprop: <http://dbpedia.org/property/>"
				+ "SELECT ?place WHERE { ?target dbpprop:latitude " +lat + "."
						+ " ?target dbpprop:longitude " +lon +"."
						+ "?target dbpedia2:location ?place }";
	       Query query = QueryFactory.create(queryStr);
	       
	       // Remote execution.
	       QueryExecution qexec = QueryExecutionFactory.sparqlService("http://dbpedia.org/sparql", query);
	       // Set the DBpedia specific timeout.
	       ((QueryEngineHTTP)qexec).addParam("timeout", TIMEOUT) ;

	       // Execute.
	       ResultSet rs = qexec.execSelect();
//	       ResultSetFormatter.out(System.out, rs, query);
	       
	       City city = new City();
	       while(rs.hasNext()){
	    		city.setName(rs.next().getLiteral("?place").getString());
	       }
	       
	       return city;
	}

	@Override
	public ArrayList<String> getAllGenre() {
		
		ArrayList<String> genres = new ArrayList<String>();
		
		String queryStr = "PREFIX dbpedia:<http://dbpedia.org/ontology/> PREFIX prop:<http://dbpedia.org/property/>"
				+ "Select DISTINCT ?genreName WHERE { "
				+ "?s dbpedia:genre ?genre."
				+ "?genre prop:name ?genreName }";
		
	       Query query = QueryFactory.create(queryStr);
	       
	       // Remote execution.
	       QueryExecution qexec = QueryExecutionFactory.sparqlService("http://dbpedia.org/sparql", query);
	       // Set the DBpedia specific timeout.
	       ((QueryEngineHTTP)qexec).addParam("timeout", TIMEOUT) ;

	       // Execute.
	       ResultSet rs = qexec.execSelect();
	       
	       while(rs.hasNext()){
	    		try{
	    			genres.add(rs.next().getLiteral("?genreName").getString());
	    		}
	    		catch(ClassCastException e){
	    			
	    		}
	       }
	       
	       return genres;
	}
}
